﻿/*  (c) 2008 Benjamin Schroeter
 *  This file is part of JaDAL - Just another DSL-Tools Addon Library
 *  and licensed under the New BSD License
 *  For more information please visit http://www.codeplex.com/JaDAL */
using System;
using System.ComponentModel;

namespace BenjaminSchroeter.Dsl.UserRestrictions
{
    /// <summary>
    /// A CustomTypeDescriptor for Shapes that will be used to restrict 
    /// the properties list.
    /// 
    /// All other methodes will be delegated to the default TypeDescriptor.
    /// </summary>
    public class RestrictedShapeTypeDescriptor : CustomTypeDescriptor
    {
        private readonly int RestrictionMode;
        private readonly object instance;
        private readonly TypeDescriptionProvider parentTypeDescriptionProvider;

        /// <summary>
        /// Initializes a new instance of the <see cref="RestrictedShapeTypeDescriptor"/> class.
        /// </summary>
        /// <param name="restricionMode">The selected restricion mode. Based on this value the properties list will be manipulated.</param>
        /// <param name="instance">The instance to create a TypeDescriptor for.</param>
        /// <param name="parent">The parent TypeDescriptorProvider.</param>
        public RestrictedShapeTypeDescriptor(int restricionMode, object instance, TypeDescriptionProvider parent)
        {
            this.RestrictionMode = restricionMode;
            this.instance = instance;
            this.parentTypeDescriptionProvider = parent;
        }

        public override PropertyDescriptorCollection GetProperties(System.Attribute[] atr)
        {
            PropertyDescriptorCollection properties =
                parentTypeDescriptionProvider.GetTypeDescriptor(instance).GetProperties(atr);

            // the RestrictedPropertyAttributes that use the RestrictionMode
            if (RestrictionMode > 0)
            {
                object[] attributes = instance.GetType().GetCustomAttributes(typeof(RestrictedPropertyAttribute), true);
                foreach (RestrictedPropertyAttribute attr in attributes)
                    if (attr.RestrictionMode == RestrictionMode)
                        ChangeProperty(attr.Restriction,attr.PropertyName , properties);
            }

            // the CustomRestrictedPropertyAttributes using the ISupportsUserRestrictions interface
            ISupportsUserRestrictions restrictionProvider = instance as ISupportsUserRestrictions;
            if (restrictionProvider != null)
            {
                object[] customattributes = instance.GetType().GetCustomAttributes(typeof (CustomRestrictedPropertyAttribute), true);
                foreach (CustomRestrictedPropertyAttribute attr in customattributes)
                {
                    // ask the instance for the restriction
                    Restriction restriction = restrictionProvider.GetRestriction(attr.PropertyName);
                    ChangeProperty(restriction, attr.PropertyName, properties);
                }
            }

            return properties;
        }

        /// <summary>
        /// Changes the property in the propertycollection.
        /// 
        /// For Restriction.Full everythings stays the same.
        /// For Restriction.Hidden the property will be removed.
        /// For Restriction.ReadOnly the property will be set to read only
        /// </summary>
        private void ChangeProperty(Restriction restriction, string propertyName, PropertyDescriptorCollection properycollection)
        {
            if ( restriction == Restriction.Full)
                return;

            PropertyDescriptor prop = properycollection.Find(propertyName, true);

            if (prop != null)
            {
                if (restriction == Restriction.Hidden)
                    properycollection.Remove(prop);
                else if (restriction == Restriction.ReadOnly)
                {
                    PropertyDescriptor propreadonly = new ReadOnlyPropertyDescriptor(prop);
                    properycollection.Remove(prop);
                    properycollection.Add(propreadonly);
                }
            }
        }

        /// <summary>
        /// Returns a collection of property descriptors for the object represented by this type descriptor.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> containing the property descriptions for the object represented by this type descriptor. The default is <see cref="F:System.ComponentModel.PropertyDescriptorCollection.Empty"/>.
        /// </returns>
        public override PropertyDescriptorCollection GetProperties()
        {
            return this.GetProperties(new Attribute[] {});
        }
    }
}
